from collections import defaultdict
from py2neo import Graph, Node, Relationship, NodeMatcher
import pandas as pd
from typing import Dict, List, Optional
import re
import os

# ====== 全局变量：节点标签 + 关系名定义 ======
# 1.节点标签
LABEL_元件 = "元件"
LABEL_元件类型 = "元件类型"
LABEL_图纸 = "图纸"
LABEL_工序 = "工序"
LABEL_工序检 = "工序检"
LABEL_工步 = "工步"
LABEL_工步检 = "工步检"  # （对应do6）
LABEL_工步记录参数 = "工步记录参数"  # （对应do6）
LABEL_终检 = "终检"
LABEL_检测项分类 = "检测项分类"
LABEL_规则 = "规则"
# ---
LABEL_设备 = "设备"
LABEL_制造资源 = "制造资源"
LABEL_辅料 = "辅料"

# 2.关系名
REL_有产品 = "属于类别"
REL_有图纸 = "有图纸"
REL_分类有工序 = "分类包含工序"
REL_工序有检项 = "有工序检项"
REL_工序有工步 = "有工步"
REL_工步有工步检 = "有工步检项"  # （对应do6）
REL_工步有记录参数 = "有记录参数"  # （对应do6）
REL_图纸有终检项 = "有终检项"  
REL_分类包含终检 = "分类包含终检"
# ---
REL_工步使用设备 = "使用设备"
REL_工步使用资源 = "使用制造资源"
REL_工步使用辅料 = "使用辅料"


# 终检 ↔ 工序检 ↔ 工步检
REL_终检包含规则 = "rule_终检分解"
REL_规则对应工序检 = "rule_生成工序检"
REL_工序检包含规则 = "rule_工序检分解"
REL_规则对应工步检 = "rule_生成工步检"

# 3.格外关系:自连接
REL_工序先后顺序 = "工序_先后顺序"   # 工序顺序关系 
REL_FIRST工序 = "first_工序"       # first_工序关系
REL_工步先后顺序 = "工步_先后顺序"   # 工步顺序关系 
REL_FIRST工步 = "first_工步"     # first_工步关系

import os
from dotenv import load_dotenv
load_dotenv()

NEO4J_URI = os.getenv("NEO4J_URI")
NEO4J_USER = os.getenv("NEO4J_USER")
NEO4J_PASSWORD = os.getenv("NEO4J_PASSWORD")
# 组合认证信息
NEO4J_AUTH = (NEO4J_USER, NEO4J_PASSWORD)



class Excel2Neo4j:
    """
    从多工作簿 Excel 文件读取数据并导入 Neo4j 图数据库的工具类（基于 py2neo）
    """
    def __init__(self, 
                excel_file_path: str = None):

        # self.graph = Graph(uri, auth=(user, password))
        self.graph = Graph(NEO4J_URI, auth=NEO4J_AUTH)
        self.excel_data = None
        self.缓存 = {
            "图纸ID": None,
            "工艺ID": None,
            "元件类型节点": None,
            "图纸节点": None,
            "工序节点": {},        # {"工序名称": 节点对象}
            "工步节点": {},         # {"工步ID": 节点对象}
            "终检节点": {},         # {"main_标识ID号": 节点对象}
            "工序检节点": {},       # {"main_标识ID号": 节点对象}
            "工步检节点": {},       # {"main_标识ID号": 节点对象}
        }

        if excel_file_path:
            self._load_data(excel_file_path)


    def _load_data(self, excel_file_path: str):
        """
        私有方法：读取Excel数据 + 校验“图纸版本信息”表 + 给两个ID赋值
        """
        self.excel_data = self._read_excel_data(excel_file_path)
        print(f"✅ 已读取Excel文件，包含工作簿: \n{self.get_all_sheet_names()}")


    def get_sheet(self, sheet_name: str) -> Optional[pd.DataFrame]:
        return self.excel_data.get(sheet_name)

    def get_all_sheet_names(self) -> List[str]:
        return list(self.excel_data.keys())

    def _read_excel_data(self, excel_file_path: str) -> Dict[str, pd.DataFrame]:
        return pd.read_excel(excel_file_path, sheet_name=None)

    def clear_database(self):
        self.graph.run("MATCH (n) DETACH DELETE n")
        print("✅ 数据库已清空")
    

    def do1_图纸版本信息(self, sheet_name: str):
        """处理“图纸版本信息”工作表（超精简版）"""
        df = self.get_sheet(sheet_name)
        if df is None or df.dropna(how='all').empty: 
            print(f"⚠️ {sheet_name} 工作表为空")
            return
        
        # 取第一行数据，空值替换为空白字符串
        row = df.dropna(how='all').iloc[0].fillna("")
        props = row.to_dict()

        # 1. 创建元件节点
        node_元件 = Node(LABEL_元件, **props)
        self.graph.create(node_元件)

        # 2. 提取关键属性，更新缓存
        self.缓存["图纸ID"] = props.get("图纸ID", "")
        self.缓存["工艺ID"] = props.get("工艺ID", "")

        # 3. 创建元件类型节点并缓存
        node_元件类型 = Node(LABEL_元件类型, 产品分类=props.get("产品分类", ""), 工艺ID=self.缓存["工艺ID"])
        self.缓存["元件类型节点"] = node_元件类型
        self.graph.create(node_元件类型)
        print(f"✅ 创建元件类型节点: {node_元件类型}")

        # 4. 创建图纸节点并缓存
        node_图纸 = Node(LABEL_图纸, 图纸ID=self.缓存["图纸ID"], 工艺ID=self.缓存["工艺ID"])
        print(f"✅ 创建图纸节点: {node_图纸}")
        self.缓存["图纸节点"] = node_图纸
        self.graph.create(node_图纸)

        # 5. 建立并创建关系
        self.graph.create(Relationship(node_元件, REL_有产品, node_元件类型))
        self.graph.create(Relationship(node_元件, REL_有图纸, node_图纸))





    def do2_工艺工序信息(self, sheet_name: str):
        """处理工艺工序信息（超精简版 + 工序先后顺序 + first_工序关系）"""
        df = self.get_sheet(sheet_name)
        if df is None or df.dropna(how='all').empty:
            print(f"⚠️ {sheet_name} 工作表为空")
            return

        for _, row in df.iterrows():
            props = row.fillna("").to_dict()

            # 创建工序节点
            node_工序 = Node(LABEL_工序, **props)
            self.缓存["工序节点"][row["工序名称"]] = node_工序
            self.graph.create(node_工序)
            print(f"✅ 创建工序节点: {node_工序}")

            # 建立“产品分类 -> 工序”关系
            rel = Relationship(self.缓存["元件类型节点"], REL_分类有工序, node_工序)
            self.graph.create(rel)

        # ========= 新增逻辑开始 =========
        # 从缓存取出工序节点，按 工序号(int) 排序
        sorted_工序 = sorted(
            self.缓存["工序节点"].values(),
            key=lambda n: int(n["工序号"])
        )

        # 建立工序之间的先后关系
        for i in range(len(sorted_工序) - 1):
            current = sorted_工序[i]
            next_node = sorted_工序[i + 1]
            rel = Relationship(current, REL_工序先后顺序, next_node)
            self.graph.create(rel)

        # 建立元件类型 -> 第一个工序的关系
        if sorted_工序:
            first_工序 = sorted_工序[0]
            rel_first = Relationship(self.缓存["元件类型节点"], REL_FIRST工序, first_工序)
            self.graph.create(rel_first)
        # ========= 新增逻辑结束 =========
        
        




    def do3_终检方案明细(self, sheet_name: str):
        """处理终检方案明细（嵌套子函数版）"""
        # ========== 1. 读取数据 ==========
        df = self.get_sheet(sheet_name)
        if df is None or df.dropna(how='all').empty: 
            print(f"⚠️ {sheet_name} 工作表为空")
            return

        # ========== 2. 嵌套子函数1：创建检测项分类节点 ==========
        def _create_检测项分类节点(df):
            分类节点缓存 = {}
            原始分类列表 = df["原始分类"].dropna().unique().tolist()
            for 分类名称 in 原始分类列表:
                if 分类名称 not in 分类节点缓存:
                    node_分类节点 = Node(
                        LABEL_检测项分类,
                        分类名称=分类名称,
                        图纸ID=self.缓存.get("图纸ID"),
                        工艺ID=self.缓存.get("工艺ID")
                    )
                    self.graph.create(node_分类节点)
                    print(f"✅创建检测项分类节点: {分类名称}")
                    分类节点缓存[分类名称] = node_分类节点
            return 分类节点缓存

        # ========== 3. 嵌套子函数2：创建终检节点及关系 ==========
        def _create_终检节点及关系(df, 分类节点缓存):
            for _, row in df.iterrows():
                props = row.fillna("").to_dict() 

                当前分类名称 = props.get("原始分类")
                main_标识ID号 = props.get("main_标识ID号")  # 关键ID


                node_终检节点 = Node(LABEL_终检, **props)
                self.graph.create(node_终检节点)
                print(f"✅ 创建终检节点: {main_标识ID号}")
                self.缓存["终检节点"][main_标识ID号] = node_终检节点

                # 分类 -> 终检
                对应分类节点 = 分类节点缓存[当前分类名称]
                rel_分类到终检 = Relationship(对应分类节点, REL_分类包含终检, node_终检节点)
                self.graph.create(rel_分类到终检)

                # 图纸 -> 终检
                rel_图纸到终检 = Relationship(self.缓存["图纸节点"], REL_图纸有终检项, node_终检节点)
                self.graph.create(rel_图纸到终检)

        # ========== 4. 调用子函数执行主逻辑 ==========
        分类节点缓存 = _create_检测项分类节点(df)
        _create_终检节点及关系(df, 分类节点缓存)


    def do4_工序检信息(self, sheet_name: str):
        """处理工序检信息（精简版）"""

        df = self.get_sheet(sheet_name)
        if df is None or df.dropna(how='all').empty: 
            print(f"⚠️ {sheet_name} 工作表为空")
            return

        for _, row in df.iterrows():
            props = row.fillna("").to_dict() 
            props = {k: v for k, v in props.items() if not k.startswith("rule_")}    #去掉带rule字眼的 这是规则的信息，这里不用存储

            目标工序名称 = props.get("工序名称", "")
            main_标识ID号 = props.get("main_标识ID号")  # 关键ID
            匹配的工序节点 = self.缓存["工序节点"].get(目标工序名称)
            if not 匹配的工序节点:
                print(f"未找到对应的工序节点：{目标工序名称}")
                continue
            

            工序检节点 = Node(LABEL_工序检, **props)
            self.graph.create(工序检节点)
            self.缓存["工序检节点"][main_标识ID号] = 工序检节点
            print(f"✅ 创建工序检节点：{目标工序名称}")

            rel_工序到检 = Relationship(匹配的工序节点, REL_工序有检项, 工序检节点)
            self.graph.create(rel_工序到检)
    
    def do5_工序下工步信息(self, sheet_name: str):
        """处理工序下工步信息：创建工步节点，建立“工序→工步”的关联关系（REL_工序有工步）
        新增：工步之间按工步ID排序建立顺序关系 + 每个工序的 first_工步 关系
        """
        df = self.get_sheet(sheet_name)
        if df is None or df.dropna(how='all').empty:
            print(f"⚠️ {sheet_name} 工作表为空")
            return

        # 前置校验：确保工序节点已缓存
        if not self.缓存.get("工序节点") or len(self.缓存["工序节点"]) == 0:
            print(f"❌ 工序节点缓存为空，请先执行 do2_工艺工序信息() 导入工序数据")
            print(f"=== 「{sheet_name}」处理失败 ===\n")
            return


        已缓存工序名 = list(self.缓存["工序节点"].keys())

        # 为了后续排序，先缓存工步节点（按工序分组）
        工序到工步列表 = defaultdict(list)

        for idx, row in df.iterrows():
            props = row.fillna("").to_dict()

            当前工步名 = props.get("工步名称")
            关联工序名 = props.get("工序名称")
            当前工步ID = props.get("工步ID") 

            if not 关联工序名:
                print(f"⚠️ 第{idx+1}行工步「{当前工步名}」缺少“工序名称”，跳过")
                continue
            if 关联工序名 not in self.缓存["工序节点"]:
                print(f"⚠️ 第{idx+1}行工步「{当前工步名}」关联的工序「{关联工序名}」未缓存（已缓存工序：{已缓存工序名}），跳过")
                continue

            # 创建工步节点
            node_工步节点 = Node(LABEL_工步, **props)
            self.缓存["工步节点"][当前工步ID] = node_工步节点
            self.graph.create(node_工步节点)

            # 建立工序→工步关系
            关联工序节点 = self.缓存["工序节点"][关联工序名]
            rel_工序到工步 = Relationship(关联工序节点, REL_工序有工步, node_工步节点)
            self.graph.create(rel_工序到工步)

            # 缓存工步，按工序分组
            工序到工步列表[关联工序名].append(node_工步节点)

            print(f"✅ 第{idx+1}行：创建工步「{当前工步名}」→ 关联工序「{关联工序名}」")



        # ========= 新增逻辑开始 =========
        # 遍历每个工序，按工步ID排序，建立顺序关系和 first_工步 关系
        for 工序名, 工步列表 in 工序到工步列表.items():
            # 按工步ID转 int 排序
            sorted_工步 = sorted(工步列表, key=lambda n: n["工步ID"])

            # 建立工步之间的先后关系
            for i in range(len(sorted_工步) - 1):
                current = sorted_工步[i]
                next_node = sorted_工步[i + 1]
                rel = Relationship(current, REL_工步先后顺序, next_node)
                self.graph.create(rel)

            # 建立工序 -> 第一个工步的关系
            if sorted_工步:
                first_工步 = sorted_工步[0]
                工序节点 = self.缓存["工序节点"][工序名]
                rel_first = Relationship(工序节点, REL_FIRST工步, first_工步)
                self.graph.create(rel_first)
        # ========= 新增逻辑结束 =========
    



    def do6_工步检(self, sheet_name: str):
        """处理工步检信息：创建工步检节点并关联工步，通过“工步ID”匹配缓存中的工步节点，建立“工步→工步检”关系（REL_工步有工步检）
        说明：不缓存工步检节点，仅按需创建
        """
        print(f"=== 开始处理「{sheet_name}」===")
        # 1. 读取并清理工作表（判空逻辑）
        df = self.get_sheet(sheet_name)
        if df is None or df.dropna(how='all').empty:
            print(f"⚠️ {sheet_name} 工作表为空")
            return

        # 直接使用原始 DataFrame，无需筛选
        df_工步检 = df.dropna(how='all')
        if df_工步检.empty:
            print(f"⚠️ 工作表「{sheet_name}」中无有效数据行，跳过处理")
            return

        # 3. 前置校验：确保工步节点已缓存（依赖do5执行结果）
        if not self.缓存.get("工步节点") or len(self.缓存["工步节点"]) == 0:
            print(f"❌ 工步节点缓存为空，请先执行 do5_工序下工步信息() 导入工步数据")
            print(f"=== 「{sheet_name}」处理失败 ===\n")
            return

        工步检总数 = 0
        已缓存工步ID = list(self.缓存["工步节点"].keys())  # 用于报错提示

        # 4. 遍历数据，创建节点并关联
        for idx, row in df_工步检.iterrows():
            props = row.fillna("").to_dict()
            props = {k: v for k, v in props.items() if not k.startswith("rule_")} # 去掉带rule字眼的 这是规则的信息，这里不用存储
            

            # 提取关键信息：工步检名称（用于标识）、工步ID（用于关联工步）
            当前工步检名 = props.get("记录项检验项名称", f"未命名工步检_{idx+1}")
            关联工步ID = props.get("工步ID")  # 核心关联字段：通过工步ID匹配工步节点
            main_标识ID号 = props.get("main_标识ID号")  # 关键ID

            # 5. 异常校验：避免无效关联
            if 关联工步ID is None or str(关联工步ID).strip() == "":
                print(f"⚠️ 第{idx+1}行工步检「{当前工步检名}」缺少“工步ID”，跳过")
                continue
            # 检查工步ID是否在缓存中（避免关联不存在的工步）
            if 关联工步ID not in self.缓存["工步节点"]:
                print(f"⚠️ 第{idx+1}行工步检「{当前工步检名}」关联的工步ID「{关联工步ID}」未缓存（已缓存工步ID：{已缓存工步ID}），跳过")
                continue

            # 6. 核心逻辑：创建工步检节点 + 建立关联关系（无缓存）
            # 6.1 创建工步检节点（直接创建，不存入缓存）

            node_工步检 = Node(LABEL_工步检, **props)
            self.缓存["工步检节点"][main_标识ID号] = node_工步检
            self.graph.create(node_工步检)

            # 6.2 从缓存获取关联的工步节点
            关联工步节点 = self.缓存["工步节点"][关联工步ID]

            # 6.3 建立“工步→工步检”的关系
            rel_工步到工步检 = Relationship(关联工步节点, REL_工步有工步检, node_工步检)
            self.graph.create(rel_工步到工步检)

            # 7. 统计并打印进度
            工步检总数 += 1
            print(f"✅ 第{idx+1}行：创建工步检 {main_标识ID号}「{当前工步检名}」→ 关联工步（工步ID：{关联工步ID}）")

        # 8. 处理完成汇总
        print(f"\n=== 「{sheet_name}」处理完成！共创建 {工步检总数} 个工步检节点 ===")
        # 统计已关联的工步ID数量
        已关联工步ID列表 = [props.get("工步ID") for _, props in df_工步检.iterrows() if props.get("工步ID") in 已缓存工步ID]
        print(f"已关联的工步ID数量：{len(set(已关联工步ID列表))} 个\n")

    def do6_工步过程记录项(self, sheet_name: str):
        """处理工步过程记录项：创建工步记录参数节点并关联工步
        关联逻辑：通过“工步ID”匹配缓存中的工步节点，建立“工步→工步记录参数”关系（REL_工步有记录参数）
        说明：不缓存工步记录参数节点，仅按需创建
        """
        print(f"=== 开始处理「{sheet_name}」===")
        # 1. 读取并清理工作表（判空逻辑）
        df = self.get_sheet(sheet_name)
        if df is None or df.dropna(how='all').empty:
            print(f"⚠️ {sheet_name} 工作表为空")
            return

        df_记录项 = df.dropna(how='all')
        if df_记录项.empty:
            print(f"⚠️ 工作表「{sheet_name}」中无有效数据行，跳过处理")
            return

        # 2. 前置校验：确保工步节点已缓存（依赖do5执行结果）
        if not self.缓存.get("工步节点") or len(self.缓存["工步节点"]) == 0:
            print(f"❌ 工步节点缓存为空，请先执行 do5_工序下工步信息() 导入工步数据")
            print(f"=== 「{sheet_name}」处理失败 ===\n")
            return

        记录项总数 = 0
        已缓存工步ID = list(self.缓存["工步节点"].keys())  # 用于报错提示

        # 3. 遍历数据，创建节点并关联
        for idx, row in df_记录项.iterrows():
            props = row.fillna("").to_dict()

            # 提取关键信息：记录项名称（用于标识）、工步ID（用于关联工步）
            当前记录项名 = props.get("记录项检验项名称", f"未命名记录项_{idx+1}")
            关联工步ID = props.get("工步ID")

            # 4. 异常校验：避免无效关联
            if 关联工步ID is None or str(关联工步ID).strip() == "":
                print(f"⚠️ 第{idx+1}行记录项「{当前记录项名}」缺少“工步ID”，跳过")
                continue
            if 关联工步ID not in self.缓存["工步节点"]:
                print(f"⚠️ 第{idx+1}行记录项「{当前记录项名}」关联的工步ID「{关联工步ID}」未缓存（已缓存工步ID：{已缓存工步ID}），跳过")
                continue

            # 5. 创建工步记录参数节点
            node_记录项 = Node(LABEL_工步记录参数, **props)
            self.graph.create(node_记录项)

            # 6. 建立“工步 → 工步记录参数”关系
            关联工步节点 = self.缓存["工步节点"][关联工步ID]
            rel_工步到记录项 = Relationship(关联工步节点, REL_工步有记录参数, node_记录项)
            self.graph.create(rel_工步到记录项)

            # 7. 统计
            记录项总数 += 1
            print(f"✅ 第{idx+1}行：创建记录项「{当前记录项名}」→ 关联工步（工步ID：{关联工步ID}）")

        # 8. 汇总
        print(f"\n=== 「{sheet_name}」处理完成！共创建 {记录项总数} 个工步记录参数节点 ===")
        已关联工步ID列表 = [props.get("工步ID") for _, props in df_记录项.iterrows() if props.get("工步ID") in 已缓存工步ID]
        print(f"已关联的工步ID数量：{len(set(已关联工步ID列表))} 个\n")
    


    def do7_终检工序检映射(self, sheet_name: str):
        """
        处理终检与工序检的映射关系（支持一个工序检对应多个终检）
        """
        print(f"=== 开始处理「{sheet_name}」===")
        df = self.get_sheet(sheet_name)
        if df is None or df.dropna(how='all').empty:
            print(f"⚠️ {sheet_name} 工作表为空")
            return

        for _, row in df.iterrows():
            props = row.fillna("").to_dict()

            # 获取工序检ID（main_标识ID号）
            工序检映射ID = props.get("main_标识ID号")
            if not 工序检映射ID:
                print(f"⚠️ 缺少 main_标识ID号，跳过该行")
                continue

            # 获取终检ID（可能是多个，用逗号分隔）
            终检标识ID_原始 = props.get("main_映射", "")

            # 如果是“无”或者空，则跳过
            if str(终检标识ID_原始).strip().lower() in ("无", ""):
                print(f"⚠️ 工序检ID {工序检映射ID} 没有对应的终检，跳过")
                continue

            # 分割多个终检ID
            终检标识ID列表 = [
                id.strip()
                for id in str(终检标识ID_原始).split(",")
                if id.strip()
            ]

            # 从缓存获取工序检节点
            工序检节点 = self.缓存["工序检节点"].get(工序检映射ID)
            if not 工序检节点:
                print(f"⚠️ 工序检节点缓存中未找到 ID: {工序检映射ID}")
                continue

            # 为每个终检ID创建关系
            for 终检标识ID in 终检标识ID列表:
                终检节点 = self.缓存["终检节点"].get(终检标识ID)
                if not 终检节点:
                    print(f"⚠️ 终检节点缓存中未找到 ID: {终检标识ID}")
                    continue

                # 提取规则属性（列名包含 "rule"）
                规则属性 = {
                    key: value
                    for key, value in props.items()
                    if "rule" in key.lower() and value
                }

                # 创建规则节点
                规则节点 = Node(LABEL_规则, **规则属性)
                self.graph.create(规则节点)

                # 建立关系
                self.graph.create(Relationship(终检节点, REL_终检包含规则, 规则节点))
                self.graph.create(Relationship(规则节点, REL_规则对应工序检, 工序检节点))

                print(f"✅ 新建规则节点:{规则属性['rule_name']}关联终检ID: {终检标识ID} 与工序检ID: {工序检映射ID}")

    def do8_工序检工步检映射(self, sheet_name: str):
        """
        处理工序检与工步检的映射关系（支持一个工步检对应多个工序检）
        """
        print(f"=== 开始处理「{sheet_name}」===")
        df = self.get_sheet(sheet_name)
        if df is None or df.dropna(how='all').empty:
            print(f"⚠️ {sheet_name} 工作表为空")
            return

        for _, row in df.iterrows():
            props = row.fillna("").to_dict()

            # 获取工步检ID（main_标识ID号）
            工步检映射ID = props.get("main_标识ID号")
            if not 工步检映射ID:
                print(f"⚠️ 缺少 main_标识ID号，跳过该行")
                continue

            # 获取工序检ID（可能是多个，用逗号分隔）
            工序检标识ID_原始 = props.get("main_映射", "")

            # 如果是“无”或者空，则跳过
            if str(工序检标识ID_原始).strip().lower() in ("无", ""):
                print(f"⚠️ 工步检ID {工步检映射ID} 没有对应的工序检，跳过")
                continue

            

            # 分割多个工序检ID（如中文逗号， 英文逗号, 和顿号、）
            工序检标识ID列表 = [
                id.strip()
                for id in re.split(r'[,\，、]', str(工序检标识ID_原始))
                if id.strip()
            ]

            # 从缓存获取工步检节点
            工步检节点 = self.缓存["工步检节点"].get(工步检映射ID)
            if not 工步检节点:
                print(f"⚠️ 工步检节点缓存中未找到 ID: {工步检映射ID}")
                continue

            # 为每个工序检ID创建关系
            for 工序检标识ID in 工序检标识ID列表:
                工序检节点 = self.缓存["工序检节点"].get(工序检标识ID)
                if not 工序检节点:
                    print(f"⚠️ 工序检节点缓存中未找到 ID: {工序检标识ID}")
                    continue

                # 提取规则属性（列名包含 "rule"）
                规则属性 = {
                    key: value
                    for key, value in props.items()
                    if "rule" in key.lower() and value
                }

                # 创建规则节点
                规则节点 = Node(LABEL_规则, **规则属性)
                self.graph.create(规则节点)

                # 建立关系
                self.graph.create(Relationship(工序检节点, REL_工序检包含规则, 规则节点))
                self.graph.create(Relationship(规则节点, REL_规则对应工步检, 工步检节点))

                print(f"✅ 新建规则节点:{规则属性.get('rule_name','')} 关联工序检ID: {工序检标识ID} 与工步检ID: {工步检映射ID}")


    def do9_工步资源关联(self, sheet_name: str):
        """
        工步与设备、制造资源、辅料关联（设备独立唯一，仅保留名称属性）
        """
        print(f"=== 开始处理「{sheet_name}」：工步与设备/资源/辅料关联 ===")
        df = self.get_sheet(sheet_name)

        # 1. 基础数据校验
        if df is None or df.dropna(how='all').empty:
            print(f"⚠️ {sheet_name} 工作表为空")
            return
        required_cols = ["工步ID", "工步使用设备类型", "工步使用制造资源", "工步使用辅料类型"]
        missing_cols = [col for col in required_cols if col not in df.columns]
        if missing_cols:
            print(f"❌ 工作表缺少必要列：{missing_cols}，请检查Excel格式")
            return
        if not self.缓存.get("工步节点") or len(self.缓存["工步节点"]) == 0:
            print(f"❌ 工步节点缓存为空，请先执行 do5_工序下工步信息() 导入工步数据")
            return

        # 初始化节点匹配器（用于查询数据库中已存在的节点）
        node_matcher = NodeMatcher(self.graph)

        # ---------------------- 内部子函数1：处理设备（独立唯一，仅保留名称） ----------------------
        def _process_设备():
            print("\n----- 开始处理设备 -----")
            for _, row in df.iterrows():
                工步ID = str(row["工步ID"]).strip()
                设备原始值 = str(row["工步使用设备类型"]).strip()

                # 跳过空工步ID或空设备
                if 工步ID in ("nan", "") or 设备原始值 in ("nan", "无", ""):
                    continue

                # 从缓存获取当前工步节点（工步已提前导入）
                工步节点 = self.缓存["工步节点"].get(工步ID)
                if not 工步节点:
                    print(f"⚠️ 工步ID「{工步ID}」未在缓存中找到，跳过设备关联")
                    continue

                # 分割当前行的多设备（支持中文逗号、英文逗号）
                单行列设备 = [dev.strip() for dev in re.split(r'[,\，]', 设备原始值) if dev.strip()]
                for 设备名称 in 单行列设备:
                    # MERGE 设备节点：数据库中存在则复用，不存在则新建（仅用名称作为唯一标识）
                    node_设备 = node_matcher.match(LABEL_设备, 设备名称=设备名称).first()

                    if not node_设备:
                        # 新建设备节点：仅保留“设备名称”属性
                        node_设备 = Node(LABEL_设备, 设备名称=设备名称)
                        self.graph.create(node_设备)
                        print(f"✅ 新建设备节点：{设备名称}（全局唯一）")
                    else:
                        print(f"ℹ️ 复用设备节点：{设备名称}（已存在）")

                    # 建立“工步→设备”关系（避免重复创建）
                    关系是否存在 = self.graph.match(
                        (工步节点, node_设备), 
                        r_type=REL_工步使用设备
                    ).first()
                    if not 关系是否存在:
                        rel = Relationship(工步节点, REL_工步使用设备, node_设备)
                        self.graph.create(rel)
                        print(f"✅ 工步ID「{工步ID}」关联设备：{设备名称}")
                    else:
                        print(f"ℹ️ 工步ID「{工步ID}」与设备「{设备名称}」的关系已存在，跳过")

        # ---------------------- 内部子函数2：处理制造资源（同设备逻辑，仅保留名称） ----------------------
        def _process_制造资源():
            print("\n----- 开始处理制造资源 -----")
            for _, row in df.iterrows():
                工步ID = str(row["工步ID"]).strip()
                资源原始值 = str(row["工步使用制造资源"]).strip()

                if 工步ID in ("nan", "") or 资源原始值 in ("nan", "无", ""):
                    continue

                工步节点 = self.缓存["工步节点"].get(工步ID)
                if not 工步节点:
                    print(f"⚠️ 工步ID「{工步ID}」未在缓存中找到，跳过资源关联")
                    continue

                单行列资源 = [res.strip() for res in re.split(r'[,\，]', 资源原始值) if res.strip()]
                for 资源名称 in 单行列资源:
                    # MERGE 资源节点：仅用名称作为唯一标识
                    node_资源 = node_matcher.match(LABEL_制造资源, 资源名称=资源名称).first()

                    if not node_资源:
                        node_资源 = Node(LABEL_制造资源, 资源名称=资源名称)
                        self.graph.create(node_资源)
                        print(f"✅ 新建制造资源节点：{资源名称}（全局唯一）")
                    else:
                        print(f"ℹ️ 复用制造资源节点：{资源名称}（已存在）")

                    # 建立“工步→资源”关系
                    关系是否存在 = self.graph.match(
                        (工步节点, node_资源), 
                        r_type=REL_工步使用资源
                    ).first()
                    if not 关系是否存在:
                        rel = Relationship(工步节点, REL_工步使用资源, node_资源)
                        self.graph.create(rel)
                        print(f"✅ 工步ID「{工步ID}」关联制造资源：{资源名称}")
                    else:
                        print(f"ℹ️ 工步ID「{工步ID}」与资源「{资源名称}」的关系已存在，跳过")

        # ---------------------- 内部子函数3：处理辅料（同设备逻辑，仅保留名称） ----------------------
        def _process_辅料():
            print("\n----- 开始处理辅料 -----")
            for _, row in df.iterrows():
                工步ID = str(row["工步ID"]).strip()
                辅料原始值 = str(row["工步使用辅料类型"]).strip()

                if 工步ID in ("nan", "") or 辅料原始值 in ("nan", "无", ""):
                    continue

                工步节点 = self.缓存["工步节点"].get(工步ID)
                if not 工步节点:
                    print(f"⚠️ 工步ID「{工步ID}」未在缓存中找到，跳过辅料关联")
                    continue

                单行列辅料 = [mat.strip() for mat in re.split(r'[,\，]', 辅料原始值) if mat.strip()]
                for 辅料名称 in 单行列辅料:
                    # MERGE 辅料节点：仅用名称作为唯一标识
                    node_辅料 = node_matcher.match(LABEL_辅料, 辅料名称=辅料名称).first()

                    if not node_辅料:
                        node_辅料 = Node(LABEL_辅料, 辅料名称=辅料名称)
                        self.graph.create(node_辅料)
                        print(f"✅ 新建辅料节点：{辅料名称}（全局唯一）")
                    else:
                        print(f"ℹ️ 复用辅料节点：{辅料名称}（已存在）")

                    # 建立“工步→辅料”关系
                    关系是否存在 = self.graph.match(
                        (工步节点, node_辅料), 
                        r_type=REL_工步使用辅料
                    ).first()
                    if not 关系是否存在:
                        rel = Relationship(工步节点, REL_工步使用辅料, node_辅料)
                        self.graph.create(rel)
                        print(f"✅ 工步ID「{工步ID}」关联辅料：{辅料名称}")
                    else:
                        print(f"ℹ️ 工步ID「{工步ID}」与辅料「{辅料名称}」的关系已存在，跳过")

        # ========== 调用三个内部子函数，执行关联逻辑 ==========
        _process_设备()
        _process_制造资源()
        _process_辅料()

        print(f"\n=== 「{sheet_name}」处理完成！===")



def do_导入数据到Neo4j(file_path):
    
    importer = Excel2Neo4j(file_path)

    importer.do1_图纸版本信息("图纸版本信息")
    importer.do2_工艺工序信息("工艺工序信息")
    importer.do3_终检方案明细("终检方案明细_small")
    importer.do4_工序检信息("工序检信息_small")
    importer.do5_工序下工步信息("工序下工步信息_small")
    importer.do6_工步检("工步检_small")
    importer.do6_工步过程记录项("工步过程记录项_small")
    importer.do7_终检工序检映射("工序检信息_small")
    importer.do8_工序检工步检映射("工步检_small")
    importer.do9_工步资源关联("工步设备资源辅料_small")




if __name__ == "__main__":
    importer = Excel2Neo4j()
    importer.clear_database()


    import os

    # 假设项目根目录为当前目录，您可以根据实际情况修改
    project_root = os.path.dirname(os.path.abspath(__file__))

    # 使用 os.path.join 构建绝对路径，兼容 Linux 和 Windows
    a_path = os.path.join(project_root, '[工艺4]--[球面透镜类]--[空间滤波器透镜L1(Z)]', '工艺-单个数据5-标注.xlsx')
    b_path = os.path.join(project_root, '[工艺3]--[非球面透镜类]--[打靶透镜]', '工艺-单个数据5-标注.xlsx')
    c_path = os.path.join(project_root, '[工艺21]--[平面反射类]--[靶场传输反射镜TM4-5基板]', '工艺-单个数据5-标注.xlsx')

    do_导入数据到Neo4j(a_path)
    do_导入数据到Neo4j(b_path)
    do_导入数据到Neo4j(c_path)


